import React, { useLayoutEffect, useRef, useState } from "react";
import * as THREE from "three";
import bgImg from "@assets/images/threejsBG.png";
import styles from "./style.module.css";

const SEPARATION = 100;
const SCREEN_WIDTH = window.innerWidth;
const SCREEN_HEIGHT = window.innerHeight;

type ThreeJsProps = {
  amountX?: number;
  amountY?: number;
  color?: string;
  top?: number;
};

const defaultProps: ThreeJsProps = {
  amountX: 50,
  amountY: 50,
  color: "#097bdb",
  top: 350,
};

const ThreeJs: React.FC<ThreeJsProps> = function ThreeJs(props) {
  const { amountX, amountY, color, top } = props;
  const box = useRef(null);
  const container = useRef(null);

  useLayoutEffect(() => {
    const scene = new THREE.Scene();

    const camera = new THREE.PerspectiveCamera(75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 1000);
    camera.position.z = 1000;

    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT - top);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setClearAlpha(0);

    container.current = document.createElement("div");
    container.current.appendChild(renderer.domElement);
    box.current.appendChild(container.current);

    const { positions, scales } = initPositions();
    const geometry = new THREE.BufferGeometry();
    geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
    geometry.setAttribute("scale", new THREE.BufferAttribute(scales, 1));

    // 初始化粒子材质
    const material = new THREE.ShaderMaterial({
      uniforms: {
        color: { value: new THREE.Color(color) },
      },
      vertexShader: `
        attribute float scale;
        void main() {
          vec4 mvPosition = modelViewMatrix * vec4( position, 2.0 );
          gl_PointSize = scale * ( 300.0 / - mvPosition.z );
          gl_Position = projectionMatrix * mvPosition;
        }
      `,
      fragmentShader: `
        uniform vec3 color;
        void main() {
          if ( length( gl_PointCoord - vec2( 0.5, 0.5 ) ) > 0.475 ) discard;
          gl_FragColor = vec4( color, 1.0 );
        }
      `,
    });
    const particles = new THREE.Points(geometry, material);
    scene.add(particles);

    let count = 0;
    let mouseX = 0;
    let windowHalfX = window.innerWidth / 2;

    const render = () => {
      camera.position.x += (mouseX - camera.position.x) * 0.05;
      camera.position.y = 300;
      camera.lookAt(scene.position);
      const positions = particles.geometry.attributes.position.array;
      const scales = particles.geometry.attributes.scale.array;
      // 计算粒子位置及大小
      let i = 0;
      let j = 0;
      for (let ix = 0; ix < amountX; ix += 1) {
        for (let iy = 0; iy < amountY; iy += 1) {
          positions[i + 1] = Math.sin((ix + count) * 0.3) * 100 + Math.sin((iy + count) * 0.5) * 100;
          scales[j] = (Math.sin((ix + count) * 0.3) + 1) * 8 + (Math.sin((iy + count) * 0.5) + 1) * 8;
          i += 3;
          j += 1;
        }
      }
      // 重新渲染粒子
      particles.geometry.attributes.position.needsUpdate = true;
      particles.geometry.attributes.scale.needsUpdate = true;
      renderer.render(scene, camera);
      count += 0.1;
    };
    function animate() {
      requestAnimationFrame(animate);
      render();
    }
    animate();

    const onDocumentMouseMove = (event) => {
      console.log("onDocumentMouseMove");
      mouseX = event.clientX - windowHalfX;
    };
    const onWindowResize = () => {
      console.log("onWindowResize");
      windowHalfX = window.innerWidth / 2;
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    };

    document.addEventListener("mousemove", onDocumentMouseMove, { passive: false });
    window.addEventListener("resize", onWindowResize, { passive: false });

    return () => {
      document.removeEventListener("mousemove", onDocumentMouseMove);
      window.removeEventListener("resize", onWindowResize);
    };
  }, []);

  // 初始化粒子位置和大小
  function initPositions() {
    const numParticles = amountX * amountY;
    const positions = new Float32Array(numParticles * 3);
    const scales = new Float32Array(numParticles);
    let i = 0;
    let j = 0;
    for (let ix = 0; ix < amountX; ix += 1) {
      for (let iy = 0; iy < amountY; iy += 1) {
        positions[i] = ix * SEPARATION - (amountX * SEPARATION) / 2;
        positions[i + 1] = 0;
        positions[i + 2] = iy * SEPARATION - (amountY * SEPARATION) / 2;
        scales[j] = 1;
        i += 3;
        j += 1;
      }
    }
    return { positions, scales };
  }

  useLayoutEffect(() => {
    container.current.style.position = "relative";
    container.current.style.top = `${top}px`;
    container.current.style.height = `${SCREEN_HEIGHT - top}px`;
  }, [top]);

  return <div ref={box} className={styles.container} />;
};

ThreeJs.defaultProps = defaultProps;
export default ThreeJs;
